﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace HackBoy_Implementation
{
    [StructLayout(LayoutKind.Explicit)]
    public struct Param_t
    {
        [FieldOffset(0)]
        public Int32 i;
        [FieldOffset(0)]
        public UInt32 d;
        [FieldOffset(0)]
        public float f;

        public Param_t(object Param)
        {
            i = 0;
            d = 0;
            f = 0;

            if (Param.GetType() == typeof(Int32))
            {
                i = Convert.ToInt32(Param);
            }
            else if (Param.GetType() == typeof(UInt32))
            {
                d = Convert.ToUInt32(Param);
            }
            else if (Param.GetType() == typeof(Single))
            {
                f = Convert.ToSingle(Param);
            }
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct BaseMessage
    {
        public ushort header;
        public ushort type;
        public Param_t param1, param2, param3, param4, param5;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
        public String buffer;

        public Param_t this[int i]
        {
            get
            {
                switch (i)
                {
                    case 1:
                        return param1;
                    case 2:
                        return param2;
                    case 3:
                        return param3;
                    case 4:
                        return param4;
                    case 5:
                        return param5;
                    default:
                        return new Param_t();
                }
            }
            set
            {
                switch (i)
                {
                    case 1:
                        param1 = value;
                        break;
                    case 2:
                        param2 = value;
                        break;
                    case 3:
                        param3 = value;
                        break;
                    case 4:
                        param4 = value;
                        break;
                    case 5:
                        param5 = value;
                        break;
                }
            }
        }
    }

    public class PipeSystem
    {
        #region DLL_Imports
        [DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
        private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, [Out] int lpNumberOfBytesRead);
        [DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
        private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
        #endregion

        private NamedPipeClientStream _pipe = null;
        private Mutex _mutex = new Mutex();

        public BaseMessage InBuffer = new BaseMessage();
        public String sClientTitle; //"Battle of Immortals-"
        public String sCharName {get;set;}

        public PipeSystem(string pClientTitle)
        { this.sClientTitle = pClientTitle; }

        private bool IsPipeConnectedOrCreated(String pipeName)
        {
            try
            {
                if (_pipe == null || _pipe.IsConnected == false)
                {
                    _pipe = new NamedPipeClientStream(".", pipeName);
                    _pipe.Connect(1000);
                }

                if (_pipe.IsConnected && (_pipe.CanRead || _pipe.CanWrite))
                {
                    return true;
                }
            }
            catch (Exception exception)
            {
                Debug.Print(exception.Message);
            }
            return false;
        }

        private bool IsPipeCreated()
        {
            try
            {
                if (_pipe == null)
                    return false;
                return true;
            }
            catch (Exception exception)
            {
                Debug.Print(exception.Message);
            }
            return false;
        }

        private bool Connect()
        {
            if (!IsPipeCreated())
            {
                try
                {
                    Process[] processes = Process.GetProcessesByName("Game");
                    foreach (Process p in processes)
                    {
                        if (sCharName != null && sCharName.Length > 0)
                        {
                            IntPtr hProcess = OpenProcess(0x0010 /*PROCESS_VM_READ*/, false, p.Id);
                            if (hProcess.ToInt32() != -1 /*INVALID_HANDLE_VALUE*/)
                            {
                                byte[] buffer = new byte[20];
                                ReadProcessMemory(hProcess, (IntPtr)0xBD86D0, buffer, 16, 0);
                                String sName = "";
                                foreach (byte b in buffer)
                                {
                                    if (b == 0)
                                        break;
                                    sName += (char)b;
                                }
                                if (sName.Equals(sCharName))
                                {
                                    _pipe = new NamedPipeClientStream(".", "boy_" + p.Id.ToString());
                                    break;
                                }
                            }
                        }
                        else
                        {
                            if (p.MainWindowTitle.StartsWith(sClientTitle))
                            {
                                _pipe = new NamedPipeClientStream(".", "boy_" + p.Id.ToString());
                                break;
                            }
                        }
                    }
                    _pipe.Connect(1000);
                    if (!_pipe.IsConnected)
                        return false;
                }
                catch (Exception exception)
                {
                    Debug.Print(exception.Message);
                }
            }
            return true;
        }

        public void Disconnect()
        {
            if (_pipe != null)
            {
                _pipe.Close();
                _pipe = null;
            }
        }

        public void Cmd(PipeCommands uMsg)
        {
            Cmd(uMsg, 0, 0, 0, 0, 0, "");
        }

        public void Cmd(PipeCommands uMsg, Int32 param1)
        {
            Cmd(uMsg, param1, 0, 0, 0, 0, "");
        }

        public void Cmd(PipeCommands uMsg, Int32 param1, Int32 param2)
        {
            Cmd(uMsg, param1, param2, 0, 0, 0, "");
        }

        public void Cmd(PipeCommands uMsg, String buffer)
        {
            Cmd(uMsg, 0, 0, 0, 0, 0, buffer);
        }

        public void Cmd(PipeCommands uMsg, String buffer, Int32 param1)
        {
            Cmd(uMsg, param1, 0, 0, 0, 0, buffer);
        }

        public void Cmd(PipeCommands uMsg, Int32 param1, Int32 param2, Int32 param3, Int32 param4, Int32 param5, String buffer)
        {
            SendMessage((MessageType.IS_NUMERIC | MessageType.IS_COMMAND), uMsg,
                new Param_t(param1), new Param_t(param2), new Param_t(param3), new Param_t(param4), new Param_t(param5),
                buffer);
        }

        public BaseMessage CmdCB(PipeCommands uMsg)
        {
            return CmdCB(uMsg, 0, 0, 0, 0, 0, "");
        }

        public BaseMessage CmdCB(PipeCommands uMsg, Int32 param1)
        {
            return CmdCB(uMsg, param1, 0, 0, 0, 0, "");
        }

        public BaseMessage CmdCB(PipeCommands uMsg, Int32 param1, Int32 param2)
        {
            return CmdCB(uMsg, param1, param2, 0, 0, 0, "");
        }

        public BaseMessage CmdCB(PipeCommands uMsg, String buffer)
        {
            return CmdCB(uMsg, 0, 0, 0, 0, 0, buffer);
        }

        public BaseMessage CmdCB(PipeCommands uMsg, String buffer, Int32 param1)
        {
            return CmdCB(uMsg, param1, 0, 0, 0, 0, buffer);
        }

        public BaseMessage CmdCB(PipeCommands uMsg, Int32 param1, Int32 param2, Int32 param3, Int32 param4, Int32 param5, String buffer)
        {
            SendMessage((MessageType.IS_NUMERIC | MessageType.IS_REQUEST), uMsg,
                new Param_t(param1), new Param_t(param2), new Param_t(param3), new Param_t(param4), new Param_t(param5),
                buffer);
            return InBuffer;
        }

        private bool SendMessage(MessageType msgType, PipeCommands uMsg, Param_t param1, Param_t param2, Param_t param3, Param_t param4, Param_t param5, string buffer)
        {
            _mutex.WaitOne();

            if (!IsPipeCreated() || !_pipe.IsConnected)
                Connect();

            try
            {
                if (_pipe.IsConnected)
                {
                    IntPtr PipeMessagePtr = default(IntPtr);
                    int StructSize;
                    BaseMessage OutBuffer = new BaseMessage();
                    OutBuffer.header = (ushort)uMsg;
                    OutBuffer.type = (ushort)msgType;
                    OutBuffer.param1 = param1;
                    OutBuffer.param2 = param2;
                    OutBuffer.param3 = param3;
                    OutBuffer.param4 = param4;
                    OutBuffer.param5 = param5;
                    OutBuffer.buffer = buffer;

                    try
                    {
                        StructSize = Marshal.SizeOf(OutBuffer);
                        PipeMessagePtr = Marshal.AllocHGlobal(StructSize);

                        Marshal.StructureToPtr(OutBuffer, PipeMessagePtr, false);
                        byte[] StreamData = new byte[StructSize];

                        Marshal.Copy(PipeMessagePtr, StreamData, 0, StructSize);

                        _pipe.Write(StreamData, 0, StructSize);

                        if (msgType == MessageType.IS_REQUEST)
                        {
                            _pipe.Read(StreamData, 0, StructSize);
                            Marshal.Copy(StreamData, 0, PipeMessagePtr, StructSize);
                            InBuffer = (BaseMessage)Marshal.PtrToStructure(PipeMessagePtr, typeof(BaseMessage));
                        }
                    }
                    catch (Exception exception)
                    {
                        Debug.Print(exception.Message);
                        _mutex.ReleaseMutex();
                        return false;
                    }
                    finally
                    {
                        Marshal.FreeHGlobal(PipeMessagePtr);
                    }
                }
                else
                {
                    _mutex.ReleaseMutex();
                    return false;
                }
            }
            catch (Exception exception)
            {
                Debug.Print(exception.Message);
                _mutex.ReleaseMutex();
                return false;
            }

            _mutex.ReleaseMutex();
            return true;
        }
    

    }
}
